home *** CD-ROM | disk | FTP | other *** search
/ Mac Power 1997 December / MACPOWER-1997-12.ISO.7z / MACPOWER-1997-12.ISO / AMUG / PROGRAMMING / Raven 1.2.sit / Raven 1.2 / Source / Foundation / OS / ZInterruptTimer.cpp < prev    next >
Text File  |  1997-06-18  |  10KB  |  405 lines

  1. /*
  2.  *  File:       ZInterruptTimer.cpp
  3.  *  Summary:       Interrupt driven timer classes
  4.  *  Written by: Jesse Jones
  5.  *
  6.  *  Copyright ゥ 1996-1997 Jesse Jones. 
  7.  *    For conditions of distribution and use, see copyright notice in ZTypes.h  
  8.  *
  9.  *  Change History (most recent first):    
  10.  *
  11.  *         <2>     4/13/97    JDJ        MTimeMgrTimer descends from MExitAction instead of
  12.  *                                    using an ExitToShell patch.
  13.  *         <1>     6/23/96    JDJ        Created
  14.  */
  15.  
  16. #include <ZInterruptTimer.h>
  17.  
  18. #include <SLFunctions.h>
  19. #include <ZDebug.h>
  20. #include <ZExceptions.h>
  21. #include <ZPatch.h>
  22.  
  23.  
  24. //---------------------------------------------------------------
  25. //
  26. // Types and Constants
  27. //
  28. //---------------------------------------------------------------
  29. typedef MTimeMgrTimer* TimeMgrTimerPtr;
  30.  
  31. const short kMaxTimers = 100;
  32.  
  33.  
  34. // ===================================================================================
  35. //    class MVBLTimer
  36. // ===================================================================================
  37.  
  38. //---------------------------------------------------------------
  39. //
  40. // MVBLTimer::~MVBLTimer
  41. //
  42. //---------------------------------------------------------------
  43. MVBLTimer::~MVBLTimer()
  44. {
  45.     ASSERT(!this->TimerIsRunning());            // Otherwise OnTime can be called while the object is being destroyed.
  46.     ASSERT(!mVBLInfo.inTask);            // Can't call this from within the VBL task!
  47.  
  48.     if (mTimerInstalled)
  49.         this->RemoveTimer();
  50.         
  51.     if (mVBLInfo.task.vblAddr != nil)
  52.         DisposeRoutineDescriptor(mVBLInfo.task.vblAddr);
  53. }
  54.  
  55.     
  56. //---------------------------------------------------------------
  57. //
  58. // MVBLTimer::MVBLTimer 
  59. //
  60. // Note that the timer has to start out stopped since the vtable 
  61. // entry for OnTime is still nil.
  62. //
  63. // ・・ハIt seems like we could get rid of this by providing a definition 
  64. // ・・ハfor OnTime (while keeping it pure virtual), but this doesn't
  65. // ・・ work with CW9. (Creating a base class with a definition of
  66. // ・・ハOnTime doesn't work either).
  67. //
  68. //---------------------------------------------------------------
  69. MVBLTimer::MVBLTimer(MilliSecond freq, bool running) : MBaseTimer(freq, running)
  70. {
  71.     ASSERT(freq >= 16);                    // VBL can't handle anything faster.
  72.     ASSERT(!running);
  73.  
  74.     mVBLInfo.myA5     = SetCurrentA5();
  75.     mVBLInfo.thisPtr  = this;
  76.     mVBLInfo.vblFreq  = (short) (60*freq/1000);
  77.     mVBLInfo.inTask   = false;
  78.  
  79.     mVBLInfo.task.qType    = vType;
  80.     mVBLInfo.task.vblAddr  = NewVBLProc(OnInterrupt);
  81.     mVBLInfo.task.vblCount = mVBLInfo.vblFreq;
  82.     mVBLInfo.task.vblPhase = 0;
  83.     
  84.     mTimerInstalled = false;
  85.  
  86.     if (this->TimerIsRunning())                // we have a definition of OnTime so this is OK
  87.         this->InstallTimer();
  88. }
  89.  
  90.  
  91. //---------------------------------------------------------------
  92. //
  93. // MVBLTimer::StopTimer
  94. //
  95. //---------------------------------------------------------------
  96. void MVBLTimer::StopTimer()
  97. {
  98.     this->RemoveTimer();
  99.  
  100.     Inherited::StopTimer();
  101. }
  102.  
  103.     
  104. //---------------------------------------------------------------
  105. //
  106. // MVBLTimer::StartTimer
  107. //
  108. //---------------------------------------------------------------
  109. void MVBLTimer::StartTimer()
  110. {
  111.     Inherited::StartTimer();
  112.     
  113.     this->InstallTimer();
  114. }
  115.  
  116.  
  117. //---------------------------------------------------------------
  118. //
  119. // MVBLTimer::SetTimerFreq
  120. //
  121. //---------------------------------------------------------------
  122. void MVBLTimer::SetTimerFreq(MilliSecond freq)
  123. {
  124.     ASSERT(freq >= 16);                    // VBL can't handle anything faster.
  125.  
  126.     Inherited::SetTimerFreq(freq);
  127.     
  128.     mVBLInfo.vblFreq = (short) (60*freq/1000);
  129. }
  130.  
  131.  
  132. //---------------------------------------------------------------
  133. //
  134. // MVBLTimer::InstallTimer
  135. //
  136. //---------------------------------------------------------------
  137. void MVBLTimer::InstallTimer()
  138. {
  139.     ASSERT(!mVBLInfo.inTask);                    // Can't call this from within the VBL task!
  140.  
  141.     if (!mTimerInstalled) {
  142.         OSErr err = VInstall((QElemPtr) &mVBLInfo.task);
  143.         ThrowIfOSErr(err);
  144.         
  145.         mTimerInstalled = true;
  146.     }
  147. }
  148.  
  149.  
  150. //---------------------------------------------------------------
  151. //
  152. // MVBLTimer::RemoveTimer
  153. //
  154. //---------------------------------------------------------------
  155. void MVBLTimer::RemoveTimer()
  156. {
  157.     if (mTimerInstalled) {
  158.         if (mVBLInfo.inTask)
  159.             mVBLInfo.task.vblCount = 0;            // Can't call VRemove from within the VBL task!
  160.         
  161.         else {
  162.             OSErr err = VRemove((QElemPtr) &mVBLInfo.task);
  163.             ASSERT(err == noErr);
  164.             
  165.             mTimerInstalled = false;
  166.         }
  167.     }
  168. }
  169.  
  170.  
  171. //---------------------------------------------------------------
  172. //
  173. // MVBLTimer::OnInterrupt                                [static]
  174. //
  175. //---------------------------------------------------------------
  176. #pragma profile off
  177. pascal void MVBLTimer::OnInterrupt(VBLTaskPtr taskPtr)        // interrupt code
  178. {
  179.     SVBLRecord* dataPtr = reinterpret_cast<SVBLRecord*>(taskPtr);
  180.     
  181.     volatile long oldA5 = SetA5(dataPtr->myA5);
  182.     
  183.     SLEnterInterrupt();
  184.     
  185.     SAFE_ASSERT(dataPtr != nil);
  186.     
  187.     try {
  188.         // Reset the VBL so we get called again.
  189.         dataPtr->task.vblCount = dataPtr->vblFreq;
  190.         
  191.         // Set a flag so we can complain if anyone does something they
  192.         // shouldn't while we're in the VBL interrupt.
  193.         SAFE_ASSERT(!dataPtr->inTask);
  194.         dataPtr->inTask = true;
  195.         
  196.         SAFE_ASSERT(dataPtr->thisPtr != nil);
  197.         
  198.         if (dataPtr->thisPtr->TimerIsRunning())
  199.             dataPtr->thisPtr->OnTime();
  200.  
  201.     } catch (...) {
  202.     }
  203.  
  204.     dataPtr->inTask = false;
  205.     
  206.     SLLeaveInterrupt();
  207.         
  208.     SetA5(oldA5);    
  209. }
  210. #pragma profile reset
  211.  
  212. #pragma mark -
  213.  
  214. // ===================================================================================
  215. //    class MTimeMgrTimer
  216. // ===================================================================================
  217.  
  218. //---------------------------------------------------------------
  219. //
  220. // MTimeMgrTimer::~MTimeMgrTimer
  221. //
  222. //---------------------------------------------------------------
  223. MTimeMgrTimer::~MTimeMgrTimer()
  224. {
  225.     ASSERT(!this->TimerIsRunning());        // Otherwise OnTime can be called while the object is being destroyed.
  226.     ASSERT(!mTimeMgrInfo.inTask);            // Can't call this from within the time manager task!
  227.  
  228.     if (mTimerInstalled)
  229.         this->RemoveTimer();
  230.         
  231.     if (mTimeMgrInfo.task.tmAddr != nil)
  232.         DisposeRoutineDescriptor(mTimeMgrInfo.task.tmAddr);
  233. }
  234.  
  235.     
  236. //---------------------------------------------------------------
  237. //
  238. // MTimeMgrTimer::MTimeMgrTimer
  239. //
  240. // Note that the timer has to start out stopped since the vtable 
  241. // entry for OnTime is still nil.
  242. //
  243. // ・・ハIt seems like we could get rid of this by providing a definition 
  244. // ・・ハfor OnTime (while keeping it pure virtual), but this doesn't
  245. // ・・ work with CW9. (Creating a base class with a definition of
  246. // ・・ハOnTime doesn't work either).
  247. //
  248. //---------------------------------------------------------------
  249. MTimeMgrTimer::MTimeMgrTimer(MilliSecond freq, bool running) : MBaseTimer(freq, running)
  250. {
  251.     ASSERT(!running);
  252.     
  253.     mTimeMgrInfo.task.tmAddr     = NewTimerProc(OnInterrupt);
  254.     mTimeMgrInfo.task.tmWakeUp   = 0;
  255.     mTimeMgrInfo.task.tmReserved = 0;
  256.     
  257.     mTimeMgrInfo.mMyA5   = SetCurrentA5();
  258.     mTimeMgrInfo.thisPtr = this;
  259.     mTimeMgrInfo.inTask  = false;
  260.     
  261.     mTimerInstalled = false;
  262.     
  263.     if (this->TimerIsRunning())
  264.         this->InstallTimer();
  265. }
  266.  
  267.  
  268. //---------------------------------------------------------------
  269. //
  270. // MTimeMgrTimer::StopTimer
  271. //
  272. //---------------------------------------------------------------
  273. void MTimeMgrTimer::StopTimer()
  274. {
  275.     this->RemoveTimer();
  276.  
  277.     Inherited::StopTimer();
  278. }
  279.  
  280.     
  281. //---------------------------------------------------------------
  282. //
  283. // MTimeMgrTimer::StartTimer
  284. //
  285. //---------------------------------------------------------------
  286. void MTimeMgrTimer::StartTimer()
  287. {
  288.     Inherited::StartTimer();
  289.     
  290.     this->InstallTimer();
  291. }
  292.  
  293.  
  294. //---------------------------------------------------------------
  295. //
  296. // MTimeMgrTimer::SetTimerFreq
  297. //
  298. //---------------------------------------------------------------
  299. void MTimeMgrTimer::SetTimerFreq(MilliSecond freq)
  300. {
  301.     Inherited::SetTimerFreq(freq);
  302.     
  303.     if (mTimerInstalled) {
  304.         this->RemoveTimer();
  305.         this->InstallTimer();
  306.     }
  307. }
  308.  
  309.  
  310. //---------------------------------------------------------------
  311. //
  312. // MTimeMgrTimer::InstallTimer
  313. //
  314. //---------------------------------------------------------------
  315. void MTimeMgrTimer::InstallTimer()
  316. {
  317.     ASSERT(!mTimeMgrInfo.inTask);                    // Can't call this from within the time manager task!
  318.  
  319.     if (!mTimerInstalled) {
  320.         mTimeMgrInfo.task.tmWakeUp   = 0;            // Disable drift-free execution (can cause problems with small frequencies. See IM:Processes pg 3-8, 3-9)
  321.         mTimeMgrInfo.task.tmReserved = 0;            // ・・ Might want to enable drift-free execution for large frequencies.
  322.  
  323.         mTimerInstalled = true;
  324.  
  325.         InsXTime((QElemPtr) &mTimeMgrInfo.task);
  326.         PrimeTime((QElemPtr) &mTimeMgrInfo.task, this->GetTimerFreq());
  327.     }
  328. }
  329.  
  330.  
  331. //---------------------------------------------------------------
  332. //
  333. // MTimeMgrTimer::RemoveTimer
  334. //
  335. //---------------------------------------------------------------
  336. void MTimeMgrTimer::RemoveTimer()
  337. {
  338.     if (mTimerInstalled) {
  339.         RmvTime((QElemPtr) &mTimeMgrInfo.task);    // Note that IM VI says that RmvTime can be called at interrupt time... 
  340.     
  341.         mTimerInstalled = false;
  342.     }
  343. }
  344.  
  345.  
  346. //---------------------------------------------------------------
  347. //
  348. // MTimeMgrTimer::OnInterrupt                            [static]
  349. //
  350. //---------------------------------------------------------------
  351. #pragma profile off
  352. pascal void MTimeMgrTimer::OnInterrupt(TMTask* taskPtr)        // interrupt code
  353. {
  354.     STimeMgrRecord* dataPtr = reinterpret_cast<STimeMgrRecord*>(taskPtr);
  355.  
  356.     volatile long oldA5 = SetA5(dataPtr->mMyA5);
  357.     
  358.     SLEnterInterrupt();
  359.     
  360.     SAFE_ASSERT(dataPtr != nil);
  361.     
  362.     try {
  363.         // Set a flag so we can complain if anyone does something they
  364.         // shouldn't while we're in the time manager interrupt.
  365.         SAFE_ASSERT(!dataPtr->inTask);
  366.         dataPtr->inTask = true;
  367.         
  368.         SAFE_ASSERT(dataPtr->thisPtr != nil);
  369.         
  370.         if (dataPtr->thisPtr->TimerIsRunning())
  371.             dataPtr->thisPtr->OnTime();
  372.         
  373.         // Prime the pump so we get called again.
  374.         if (dataPtr->thisPtr->TimerIsRunning())
  375.             PrimeTime((QElemPtr) &dataPtr->task, dataPtr->thisPtr->GetTimerFreq());    
  376.  
  377.     } catch (...) {
  378.     }
  379.     
  380.     dataPtr->inTask = false;
  381.     
  382.     SLLeaveInterrupt();
  383.         
  384.     SetA5(oldA5);    
  385. }
  386. #pragma profile reset
  387.  
  388.  
  389. //---------------------------------------------------------------
  390. //
  391. // MTimeMgrTimer::OnAbnormalExit
  392. //
  393. // Unlike VBL tasks, Time Manager tasks have to be removed by the 
  394. // application when it terminates (see IM Processes page 3-9).
  395. //
  396. //---------------------------------------------------------------
  397. void MTimeMgrTimer::OnAbnormalExit()
  398. {
  399.     this->RemoveTimer();
  400. }
  401.  
  402.  
  403.  
  404.  
  405.